#!/bin/bash

set -e  # 遇到错误立即退出
set -u  # 使用未定义的变量时报错

# 脚本配置变量
readonly KUBERNETES_VERSION="1.27.4"
readonly SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# 颜色定义
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m' # No Color

# 日志函数
log_info() { echo -e "${GREEN}[INFO]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_error() { echo -e "${RED}[ERROR]${NC} $*"; }
log_debug() { echo -e "${BLUE}[DEBUG]${NC} $*"; }

# 判断传递的参数
NODE_TYPE="${1:-"master"}"
FORCE_INSTALL="${2:-"false"}"  # 新增：强制安装选项，跳过交互确认

# 显示脚本用途说明
show_usage() {
    cat <<EOF
${GREEN}Kubernetes 集群安装脚本 (多发行版支持)${NC}

支持的操作系统:
- RHEL/CentOS/Rocky/AlmaLinux 7/8/9
- Amazon Linux 2/2023
- Debian 10/11/12
- Ubuntu 20.04/22.04/24.04

用法: $SCRIPT_NAME [master|worker] [force]

环境要求:
===================================================================
1. 安装集群Master节点: $SCRIPT_NAME master
2. 安装worker节点: $SCRIPT_NAME worker
3. 强制安装(跳过确认): $SCRIPT_NAME master force
4. 当前用户是 root 用户
5. 确保系统网络畅通，可以访问外部镜像源
6. 指定kubernetes安装版本: $KUBERNETES_VERSION
7. 默认使用flannel网络组件
===================================================================

当前配置:
- Kubernetes版本: $KUBERNETES_VERSION
- 节点类型: $NODE_TYPE
- 网络插件: flannel

EOF
}

# 检查当前用户是否为 root 用户
check_root_user() {
    if [[ $EUID -ne 0 ]]; then
        log_error "请使用 root 用户执行此脚本。"
        exit 1
    fi
}

# 判断是否为中国地区
is_china() {
    local timeout=5
    local china_indicators=(
        "https://ipapi.co/country/"
        "https://ifconfig.co/country"
        "http://ip-api.com/line?fields=countryCode"
    )
    
    for indicator in "${china_indicators[@]}"; do
        local country
        country=$(curl -sSL --connect-timeout $timeout "$indicator" 2>/dev/null || true)
        if [[ "$country" =~ "CN"|"China" ]]; then
            return 0
        fi
    done
    
    # 检查时区（备选方案）
    if timedatectl show 2>/dev/null | grep -q "Asia/Shanghai" || \
       [[ $(cat /etc/timezone 2>/dev/null) == "Asia/Shanghai" ]]; then
        return 0
    fi
    
    return 1
}

# 根据地区选择镜像源
select_mirrors() {
    if is_china; then
        log_info "检测在中国地区，将使用国内镜像源。"
        export DOCKER_IMAGE_REPOSITORY="registry.aliyuncs.com/google_containers"
        export YUM_REPOSITORY="https://mirrors.aliyun.com/kubernetes"
        export APT_REPOSITORY="https://mirrors.aliyun.com/kubernetes/apt"
        export FLANNEL_MANIFEST="https://gitee.com/mirrors/flannel/raw/master/Documentation/kube-flannel.yml"
        export CALICO_MANIFEST="https://docs.projectcalico.org/v3.20/manifests/calico.yaml"
        export CALICO_IMAGE_REPO="registry.cn-hangzhou.aliyuncs.com/calico"
    else
        log_info "检测不在中国地区，将使用官方镜像源。"
        export DOCKER_IMAGE_REPOSITORY="registry.k8s.io"
        export YUM_REPOSITORY="https://packages.cloud.google.com"
        export APT_REPOSITORY="https://apt.kubernetes.io"
        export FLANNEL_MANIFEST="https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml"
        export CALICO_MANIFEST="https://docs.projectcalico.org/v3.20/manifests/calico.yaml"
        export CALICO_IMAGE_REPO="docker.io"
    fi
}

# 获取操作系统信息
get_os_info() {
    if [[ -f /etc/os-release ]]; then
        source /etc/os-release
        OS_ID="${ID:-}"
        OS_VERSION_ID="${VERSION_ID:-}"
        OS_ID_LIKE="${ID_LIKE:-}"
    elif [[ -f /etc/redhat-release ]]; then
        if grep -q "CentOS" /etc/redhat-release; then
            OS_ID="centos"
            OS_VERSION_ID=$(grep -oE '[0-9]+\.[0-9]+' /etc/redhat-release | head -1)
        elif grep -q "Red Hat" /etc/redhat-release; then
            OS_ID="rhel"
            OS_VERSION_ID=$(grep -oE '[0-9]+\.[0-9]+' /etc/redhat-release | head -1)
        elif grep -q "Rocky" /etc/redhat-release; then
            OS_ID="rocky"
            OS_VERSION_ID=$(grep -oE '[0-9]+\.[0-9]+' /etc/redhat-release | head -1)
        elif grep -q "AlmaLinux" /etc/redhat-release; then
            OS_ID="almalinux"
            OS_VERSION_ID=$(grep -oE '[0-9]+\.[0-9]+' /etc/redhat-release | head -1)
        fi
    else
        log_error "无法检测操作系统类型"
        exit 1
    fi

    # 规范化版本号（主要版本）
    OS_MAJOR_VERSION=$(echo "$OS_VERSION_ID" | cut -d. -f1)
    
    # 根据ID_LIKE确定包管理器
    if [[ "$OS_ID_LIKE" =~ "debian" ]] || [[ "$OS_ID" =~ ^(debian|ubuntu)$ ]]; then
        PACKAGE_MANAGER="apt"
        OS_FAMILY="debian"
    elif [[ "$OS_ID_LIKE" =~ "rhel" ]] || [[ "$OS_ID" =~ ^(rhel|centos|rocky|almalinux|fedora|amzn)$ ]]; then
        PACKAGE_MANAGER="yum"
        OS_FAMILY="rhel"
        # Amazon Linux 2023 使用 dnf
        if [[ "$OS_ID" == "amzn" && "$OS_MAJOR_VERSION" -ge 2023 ]]; then
            PACKAGE_MANAGER="dnf"
        fi
    else
        log_error "不支持的操作系统: $OS_ID"
        exit 1
    fi

    log_info "检测到操作系统: $OS_ID $OS_VERSION_ID (家族: $OS_FAMILY, 包管理器: $PACKAGE_MANAGER)"
}

# 检查命令是否存在
command_exists() {
    command -v "$1" >/dev/null 2>&1
}

# 安装基础依赖
install_basic_dependencies() {
    log_info "安装基础依赖包..."
    
    case $OS_FAMILY in
        debian)
            apt-get update
            apt-get install -y curl wget gnupg2 software-properties-common \
                apt-transport-https ca-certificates lsb-release
            ;;
        rhel)
            if command_exists dnf; then
                dnf install -y curl wget yum-utils device-mapper-persistent-data lvm2
            else
                yum install -y curl wget yum-utils device-mapper-persistent-data lvm2
            fi
            ;;
    esac
}

# 检查是否已安装 Kubernetes
check_kubernetes_installed() {
    if command_exists kubeadm || command_exists kubectl || command_exists kubelet; then
        log_warn "已检测到已安装的 Kubernetes 组件。"
        
        # 如果是强制安装模式，自动卸载已存在的组件
        if [[ "$FORCE_INSTALL" == "force" ]]; then
            log_info "强制安装模式：自动卸载已存在的 Kubernetes 组件..."
            uninstall_kubernetes
        else
            # 交互式确认
            read -p "是否卸载已存在的 Kubernetes？(y/N): " uninstall_choice
            if [[ "${uninstall_choice:-}" =~ ^[Yy]$ ]]; then
                uninstall_kubernetes
            else
                log_info "已取消安装。"
                exit 0
            fi
        fi
    fi
}

# 卸载 Kubernetes
uninstall_kubernetes() {
    log_info "正在卸载 Kubernetes..."
    
    # 重置 kubeadm
    if command_exists kubeadm; then
        kubeadm reset -f
    fi
    
    # 删除网络插件
    if command_exists kubectl; then
        kubectl delete --ignore-not-found=true -f "$FLANNEL_MANIFEST" 2>/dev/null || true
        # 对于Calico，需要特殊处理国内镜像
        if [[ -n "${CALICO_IMAGE_REPO:-}" && "$CALICO_IMAGE_REPO" != "docker.io" ]]; then
            kubectl delete --ignore-not-found=true -f "$CALICO_MANIFEST" 2>/dev/null || true
        fi
    fi
    
    # 卸载软件包
    case $OS_FAMILY in
        debian)
            apt-get remove -y kubeadm kubelet kubectl containerd.io docker-ce || true
            apt-get autoremove -y || true
            ;;
        rhel)
            if command_exists dnf; then
                dnf remove -y kubeadm kubelet kubectl containerd.io docker-ce || true
            else
                yum remove -y kubeadm kubelet kubectl containerd.io docker-ce || true
            fi
            ;;
    esac
    
    # 清理文件和目录
    local dirs_to_clean=(
        "/etc/kubernetes"
        "/var/lib/etcd" 
        "/var/lib/kubelet"
        "/etc/cni"
        "/opt/cni"
        "$HOME/.kube"
        "/var/lib/docker"
        "/var/lib/containerd"
    )
    
    for dir in "${dirs_to_clean[@]}"; do
        if [[ -d "$dir" ]]; then
            rm -rf "$dir"
            log_debug "已删除目录: $dir"
        fi
    done
    
    # 清理包管理器缓存
    case $PACKAGE_MANAGER in
        apt) apt-get clean ;;
        yum) yum clean all ;;
        dnf) dnf clean all ;;
    esac
    
    log_info "Kubernetes 已成功卸载。"
}

# 关闭并禁用防火墙
disable_firewall() {
    log_info "配置防火墙..."
    
    case $OS_FAMILY in
        debian)
            if systemctl is-active --quiet ufw; then
                ufw disable
            fi
            ;;
        rhel)
            if systemctl is-active --quiet firewalld; then
                systemctl stop firewalld
                systemctl disable firewalld
            fi
            ;;
    esac
    
    # 清空iptables策略
    if command_exists iptables; then
        iptables -F && iptables -X && iptables -Z
        iptables -t nat -F && iptables -t nat -X && iptables -t nat -Z
        iptables -P INPUT ACCEPT
    fi
    
    # 禁用 SELinux (仅RHEL家族)
    if [[ "$OS_FAMILY" == "rhel" ]]; then
        if command_exists setenforce; then
            setenforce 0
            if [[ -f /etc/selinux/config ]]; then
                sed -i 's/^SELINUX=.*/SELINUX=permissive/g' /etc/selinux/config
            fi
        fi
    fi
    
    # 对于Amazon Linux 2023，可能需要额外处理
    if [[ "$OS_ID" == "amzn" && "$OS_MAJOR_VERSION" -ge 2023 ]]; then
        if command_exists firewall-cmd; then
            systemctl stop firewalld
            systemctl disable firewalld
        fi
    fi
}

# 关闭并禁用 Swap
disable_swap() {
    log_info "配置Swap..."
    swapoff -a
    if grep -q "swap" /etc/fstab; then
        sed -i '/swap/d' /etc/fstab
        log_info "已从 /etc/fstab 中移除swap配置"
    fi
}

# 优化内核参数
optimize_kernel() {
    log_info "优化内核参数..."
    
    local sysctl_file="/etc/sysctl.d/99-kubernetes.conf"
    cat > "$sysctl_file" << EOF
# Kubernetes 内核参数优化
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 60
net.ipv4.tcp_keepalive_probes = 10
vm.swappiness = 0
vm.max_map_count = 262144
vm.overcommit_memory = 1
vm.panic_on_oom = 0
fs.inotify.max_user_watches = 524288
fs.file-max = 2097152
EOF
    
    # 加载内核模块
    local modules=("br_netfilter" "overlay" "nf_conntrack")
    for module in "${modules[@]}"; do
        if ! lsmod | grep -q "^${module}"; then
            modprobe "$module"
            echo "$module" >> /etc/modules-load.d/kubernetes.conf
        fi
    done
    
    sysctl -p "$sysctl_file" > /dev/null 2>&1 || true
    log_info "内核参数优化完成"
}

# 安装 Containerd
install_containerd() {
    log_info "安装 Containerd..."
    
    case $OS_FAMILY in
        rhel)
            if command_exists dnf; then
                dnf install -y containerd
            else
                yum install -y containerd
            fi
            ;;
        debian)
            apt-get update
            apt-get install -y containerd
            ;;
    esac
    
    # 配置 containerd
    mkdir -p /etc/containerd
    containerd config default > /etc/containerd/config.toml 2>/dev/null || \
    containerd config default > /etc/containerd/config.toml
    
    # 配置 systemd cgroup 驱动程序
    sed -i 's#SystemdCgroup = false#SystemdCgroup = true#' /etc/containerd/config.toml
    sed -i "s#registry.k8s.io#${DOCKER_IMAGE_REPOSITORY}#g" /etc/containerd/config.toml
    
    systemctl daemon-reload
    systemctl enable containerd
    systemctl start containerd
    
    # 等待containerd启动
    for i in {1..30}; do
        if systemctl is-active --quiet containerd; then
            break
        fi
        sleep 1
    done
    
    log_info "Containerd 安装完成"
}

# 配置 Kubernetes 源
configure_kubernetes_repo() {
    log_info "配置 Kubernetes 源..."
    
    case $OS_FAMILY in
        debian)
            curl -fsSL "$APT_REPOSITORY/doc/apt-key.gpg" | gpg --dearmor -o /usr/share/keyrings/kubernetes-archive-keyring.gpg
            echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] $APT_REPOSITORY/ kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list
            apt-get update
            ;;
        rhel)
            cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=${YUM_REPOSITORY}/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=${YUM_REPOSITORY}/yum/doc/yum-key.gpg ${YUM_REPOSITORY}/yum/doc/rpm-package-key.gpg
EOF
            if command_exists dnf; then
                dnf makecache
            else
                yum makecache
            fi
            ;;
    esac
}

# 安装 Kubernetes 组件
install_kubernetes_components() {
    log_info "安装 Kubernetes 组件 (版本: $KUBERNETES_VERSION)..."
    
    case $OS_FAMILY in
        debian)
            apt-get install -y \
                "kubelet=${KUBERNETES_VERSION}-00" \
                "kubeadm=${KUBERNETES_VERSION}-00" \
                "kubectl=${KUBERNETES_VERSION}-00"
            ;;
        rhel)
            local version_suffix=""
            if [[ "$OS_ID" == "amzn" && "$OS_MAJOR_VERSION" -ge 2023 ]]; then
                version_suffix="--allowerasing"
            fi
            
            if command_exists dnf; then
                dnf install -y \
                    "kubelet-${KUBERNETES_VERSION}" \
                    "kubeadm-${KUBERNETES_VERSION}" \
                    "kubectl-${KUBERNETES_VERSION}" $version_suffix
            else
                yum install -y \
                    "kubelet-${KUBERNETES_VERSION}" \
                    "kubeadm-${KUBERNETES_VERSION}" \
                    "kubectl-${KUBERNETES_VERSION}"
            fi
            ;;
    esac
    
    # 禁用 kubelet 自动启动（等待初始化完成后再启动）
    systemctl enable kubelet
    systemctl stop kubelet
    
    # 安装 bash-completion
    if command_exists kubectl; then
        case $OS_FAMILY in
            debian)
                apt-get install -y bash-completion
                ;;
            rhel)
                if command_exists dnf; then
                    dnf install -y bash-completion
                else
                    yum install -y bash-completion
                fi
                ;;
        esac
        
        # 配置命令补全
        echo 'source <(kubectl completion bash)' >> ~/.bashrc
        echo 'alias k=kubectl' >> ~/.bashrc
        echo 'complete -o default -F __start_kubectl k' >> ~/.bashrc
    fi
    
    log_info "Kubernetes 组件安装完成"
}

# 初始化 Kubernetes 集群
initialize_kubernetes_cluster() {
    log_info "初始化 Kubernetes 集群..."
    
    # 停止 kubelet
    systemctl stop kubelet 2>/dev/null || true
    
    # 重置之前的安装
    kubeadm reset -f 2>/dev/null || true
    
    # 初始化集群
    kubeadm init \
        --kubernetes-version="v${KUBERNETES_VERSION}" \
        --image-repository="${DOCKER_IMAGE_REPOSITORY}" \
        --service-cidr="10.96.0.0/16" \
        --pod-network-cidr="10.244.0.0/16" \
        --upload-certs \
        --v=5

    # 配置 kubectl
    mkdir -p "$HOME/.kube"
    cp -f /etc/kubernetes/admin.conf "$HOME/.kube/config"
    chown "$(id -u):$(id -g)" "$HOME/.kube/config"
    
    # 启动 kubelet
    systemctl start kubelet
    
    log_info "Kubernetes 集群初始化完成"
}

# 安装网络插件
install_network_plugin() {
    local plugin="${1:-flannel}"
    
    log_info "安装网络插件: $plugin"
    
    case $plugin in
        flannel)
            kubectl apply -f "$FLANNEL_MANIFEST"
            ;;
        calico)
            if [[ -n "${CALICO_IMAGE_REPO:-}" && "$CALICO_IMAGE_REPO" != "docker.io" ]]; then
                # 下载并修改Calico manifest以使用国内镜像
                local calico_temp_file="/tmp/calico.yaml"
                curl -fsSL "$CALICO_MANIFEST" -o "$calico_temp_file"
                sed -i "s#docker.io/#${CALICO_IMAGE_REPO}/#g" "$calico_temp_file"
                kubectl apply -f "$calico_temp_file"
                rm -f "$calico_temp_file"
            else
                kubectl apply -f "$CALICO_MANIFEST"
            fi
            ;;
        *)
            log_error "不支持的网络插件: $plugin"
            return 1
            ;;
    esac
    
    # 等待网络插件就绪
    log_info "等待网络插件就绪..."
    for i in {1..60}; do
        if kubectl get pods -n kube-system -l app=flannel 2>/dev/null | grep -q "Running"; then
            break
        fi
        sleep 5
    done
    
    log_info "网络插件安装完成"
}

# 验证安装
validate_installation() {
    log_info "验证 Kubernetes 安装..."
    
    # 等待节点就绪
    for i in {1..30}; do
        if kubectl get nodes 2>/dev/null | grep -q "Ready"; then
            break
        fi
        sleep 5
    done
    
    # 显示集群信息
    echo
    kubectl cluster-info
    echo
    kubectl get nodes -o wide
    echo
    kubectl get pods -A -o wide
}

# 显示安装完成信息
show_success_info() {
    cat <<EOF

${GREEN}Kubernetes 集群安装成功！${NC}

集群信息:
- Kubernetes版本: $KUBERNETES_VERSION
- 容器运行时: Containerd
- 网络插件: flannel
- 操作系统: $OS_ID $OS_VERSION_ID

接下来您可以:
1. 查看集群状态: kubectl get nodes
2. 查看所有Pod: kubectl get pods -A
3. 加入Worker节点: kubeadm token create --print-join-command

Worker节点加入命令:
$(kubeadm token create --print-join-command 2>/dev/null || echo "请稍后运行: kubeadm token create --print-join-command")

EOF
}

# 参数验证
validate_parameters() {
    case "$NODE_TYPE" in
        master|worker) ;;
        *) log_error "无效的节点类型: $NODE_TYPE. 请使用 'master' 或 'worker'"
           show_usage
           exit 1 ;;
    esac
    
    # 验证第二个参数（如果提供）
    if [[ -n "${2:-}" && "$2" != "force" ]]; then
        log_error "无效的第二个参数: $2. 请使用 'force' 或留空"
        show_usage
        exit 1
    fi
}

# 系统兼容性检查
check_compatibility() {
    log_info "检查系统兼容性..."
    
    # 检查内核版本
    local kernel_version=$(uname -r | cut -d. -f1-2)
    local required_kernel="3.10"
    
    if (( $(echo "$kernel_version < $required_kernel" | bc -l 2>/dev/null) )); then
        log_warn "内核版本 $kernel_version 较低，建议升级到 3.10 或更高版本"
    fi
    
    # 检查内存
    local total_mem=$(grep MemTotal /proc/meminfo | awk '{print $2}')
    if [[ $total_mem -lt 2000000 ]]; then
        log_warn "系统内存小于2GB，可能影响Kubernetes正常运行"
    fi
    
    # 检查磁盘空间
    local disk_space=$(df / | awk 'NR==2 {print $4}')
    if [[ $disk_space -lt 10485760 ]]; then
        log_warn "根分区剩余空间小于10GB，建议清理空间"
    fi
}

# 主函数
main() {
    log_info "开始执行 Kubernetes 安装脚本..."
    log_debug "参数: NODE_TYPE=$NODE_TYPE, FORCE_INSTALL=$FORCE_INSTALL"
    
    show_usage
    validate_parameters "$@"
    check_root_user
    select_mirrors
    get_os_info
    check_compatibility
    
    log_info "开始安装基础依赖..."
    install_basic_dependencies
    
    log_info "检查已安装的 Kubernetes 组件..."
    check_kubernetes_installed
    
    log_info "开始系统准备工作..."
    # 系统准备
    disable_firewall
    disable_swap
    optimize_kernel
    
    log_info "配置 Kubernetes 软件源..."
    # 配置Kubernetes源
    configure_kubernetes_repo
    
    log_info "开始安装 Kubernetes 组件..."
    # 安装组件
    install_containerd
    install_kubernetes_components
    
    # 主节点特定操作
    if [[ "$NODE_TYPE" == "master" ]]; then
        log_info "开始初始化 Kubernetes 集群..."
        initialize_kubernetes_cluster
        install_network_plugin "flannel"
        validate_installation
        show_success_info
    else
        log_info "Worker节点安装完成，请使用 kubeadm join 命令加入集群"
        log_info "在Master节点运行: kubeadm token create --print-join-command 获取加入命令"
    fi
    
    log_info "Kubernetes 安装完成!"
}

# 信号处理
trap 'log_error "脚本被用户中断"; exit 1' INT TERM

# 执行主函数
main "$@"